#!/usr/bin/env python3
'''该类是ftp客户端，操作ftp服务。
可将文件发送到目标机器，或者从目标机器拉取文件。'''
import argparse
import ftplib
import os
from typing import Optional, List

class FTPClient:
    def __init__(self, host: str, username: str, password: str, port: int = 21, timeout: int = 10):
        """
        FTP客户端初始化
        
        :param host: FTP服务器地址
        :param username: 用户名
        :param password: 密码
        :param port: 端口号，默认21
        :param timeout: 超时时间(秒)，默认10
        """
        self.host = host
        self.port = port
        self.username = username
        self.password = password
        self.timeout = timeout
        self.ftp = None
        self._connect()

    def _connect(self) -> None:
        """建立FTP连接"""
        try:
            self.ftp = ftplib.FTP(timeout=self.timeout)
            self.ftp.connect(self.host, self.port)
            self.ftp.login(self.username, self.password)
            print(f"成功连接到FTP服务器 {self.host}:{self.port}")
        except Exception as e:
            raise ConnectionError(f"连接FTP服务器失败: {e}")

    def create_file(self, remote_path: str, content: str = "") -> bool:
        """
        在FTP服务器上创建文件
        
        :param remote_path: 远程文件路径
        :param content: 文件内容，默认为空
        :return: 是否创建成功
        """
        try:
            # 使用临时文件作为中介
            temp_file = "temp_ftp_upload.txt"
            with open(temp_file, 'w') as f:
                f.write(content)
            
            with open(temp_file, 'rb') as f:
                self.ftp.storbinary(f'STOR {remote_path}', f)
            
            os.remove(temp_file)
            print(f"文件创建成功: {remote_path}")
            return True
        except Exception as e:
            print(f"创建文件失败: {e}")
            if os.path.exists(temp_file):
                os.remove(temp_file)
            return False

    def upload_file(self, local_path: str, remote_path: str) -> bool:
        """
        上传本地文件到FTP服务器
        
        :param local_path: 本地文件路径
        :param remote_path: 远程文件路径
        :return: 是否上传成功
        """
        try:
            if not os.path.exists(local_path):
                print(f"本地文件不存在: {local_path}")
                return False
            
            with open(local_path, 'rb') as f:
                self.ftp.storbinary(f'STOR {remote_path}', f)
            
            print(f"文件上传成功: {local_path} -> {remote_path}")
            return True
        except Exception as e:
            print(f"文件上传失败: {e}")
            return False

    def download_file(self, remote_path: str, local_path: str) -> bool:
        """
        从FTP服务器下载文件到本地
        
        :param remote_path: 远程文件路径
        :param local_path: 本地文件路径
        :return: 是否下载成功
        """
        try:
            # 确保本地目录存在
            os.makedirs(os.path.dirname(local_path), exist_ok=True)
            
            with open(local_path, 'wb') as f:
                self.ftp.retrbinary(f'RETR {remote_path}', f.write)
            
            print(f"文件下载成功: {remote_path} -> {local_path}")
            return True
        except Exception as e:
            print(f"文件下载失败: {e}")
            return False

    def list_directory(self, remote_path: str = "") -> Optional[List[str]]:
        """
        列出FTP服务器上的目录内容
        
        :param remote_path: 远程目录路径
        :return: 目录内容列表，失败返回None
        """
        try:
            files = []
            self.ftp.dir(remote_path, files.append)
            return files
        except Exception as e:
            print(f"列出目录失败: {e}")
            return None

    def close(self) -> None:
        """关闭FTP连接"""
        if self.ftp:
            try:
                self.ftp.quit()
            except:
                self.ftp.close()
            finally:
                self.ftp = None
                print("FTP连接已关闭")

    def __enter__(self):
        """支持with语句"""
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        """支持with语句"""
        self.close()

def parse_args():
    """解析命令行参数"""
    parser = argparse.ArgumentParser(description='FTP客户端工具')
    subparsers = parser.add_subparsers(dest='command', help='可用命令', required=True)

    # 创建文件命令
    create_parser = subparsers.add_parser('create', help='在FTP服务器上创建文件')
    create_parser.add_argument('--host', required=True, help='FTP服务器地址')
    create_parser.add_argument('--port', type=int, default=21, help='FTP服务器端口 (默认: 21)')
    create_parser.add_argument('--username', required=True, help='FTP用户名')
    create_parser.add_argument('--password', required=True, help='FTP密码')
    create_parser.add_argument('--remote-path', required=True, help='要创建的远程文件路径')
    create_parser.add_argument('--content', default="", help='文件内容 (默认为空)')

    # 上传文件命令
    upload_parser = subparsers.add_parser('upload', help='上传文件到FTP服务器')
    upload_parser.add_argument('--host', required=True, help='FTP服务器地址')
    upload_parser.add_argument('--port', type=int, default=21, help='FTP服务器端口 (默认: 21)')
    upload_parser.add_argument('--username', required=True, help='FTP用户名')
    upload_parser.add_argument('--password', required=True, help='FTP密码')
    upload_parser.add_argument('--local-path', required=True, help='要上传的本地文件路径')
    upload_parser.add_argument('--remote-path', required=True, help='远程文件路径')

    # 下载文件命令
    download_parser = subparsers.add_parser('download', help='从FTP服务器下载文件')
    download_parser.add_argument('--host', required=True, help='FTP服务器地址')
    download_parser.add_argument('--port', type=int, default=21, help='FTP服务器端口 (默认: 21)')
    download_parser.add_argument('--username', required=True, help='FTP用户名')
    download_parser.add_argument('--password', required=True, help='FTP密码')
    download_parser.add_argument('--remote-path', required=True, help='要下载的远程文件路径')
    download_parser.add_argument('--local-path', required=True, help='本地文件路径')

    # 列出目录命令
    list_parser = subparsers.add_parser('list', help='列出FTP服务器目录内容')
    list_parser.add_argument('--host', required=True, help='FTP服务器地址')
    list_parser.add_argument('--port', type=int, default=21, help='FTP服务器端口 (默认: 21)')
    list_parser.add_argument('--username', required=True, help='FTP用户名')
    list_parser.add_argument('--password', required=True, help='FTP密码')
    list_parser.add_argument('--remote-path', default="", help='要列出的远程目录路径')

    return parser.parse_args()

def main():
    """主函数"""
    args = parse_args()
    
    try:
        with FTPClient(
            host=args.host,
            port=args.port,
            username=args.username,
            password=args.password
        ) as ftp:
            if args.command == 'create':
                # 创建文件
                if ftp.create_file(args.remote_path, args.content):
                    print("文件创建成功")
                else:
                    print("文件创建失败")
            
            elif args.command == 'upload':
                # 上传文件
                if ftp.upload_file(args.local_path, args.remote_path):
                    print("文件上传成功")
                else:
                    print("文件上传失败")
            
            elif args.command == 'download':
                # 下载文件
                if ftp.download_file(args.remote_path, args.local_path):
                    print("文件下载成功")
                else:
                    print("文件下载失败")
            
            elif args.command == 'list':
                # 列出目录
                files = ftp.list_directory(args.remote_path)
                if files is not None:
                    print(f"目录 {args.remote_path or '/'} 内容:")
                    for file in files:
                        print(file)
                else:
                    print("列出目录失败")
            
    except Exception as e:
        print(f"发生错误: {e}")

if __name__ == "__main__":
    main()